package pl.lodz.p.browarex.mvc;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.UUID;

import javax.inject.Inject;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import pl.lodz.p.browarex.model.AnalysisType;
import pl.lodz.p.browarex.model.Customer;
import pl.lodz.p.browarex.model.Order;
import pl.lodz.p.browarex.model.OrderProductInfo;
import pl.lodz.p.browarex.model.Product;
import pl.lodz.p.browarex.model.enums.AnalysisTypeEnum;
import pl.lodz.p.browarex.model.enums.OrderState;
import pl.lodz.p.browarex.services.interfaces.IAnalysisTypeService;
import pl.lodz.p.browarex.services.interfaces.ICustomerService;
import pl.lodz.p.browarex.services.interfaces.IOrderProductInfoService;
import pl.lodz.p.browarex.services.interfaces.IOrderService;
import pl.lodz.p.browarex.services.interfaces.IProductService;
import pl.lodz.p.browarex.services.interfaces.IUserService;
import pl.lodz.p.browarex.utils.Utils;
import pl.lodz.p.browarex.validator.OrderInfoValidator;
import pl.lodz.p.browarex.ws.BeerEndpoint;
import pl.lodz.p.browarex.ws.model.OrderRequest;

@Controller
public class BasketController {

	@Inject
	IProductService productService;
	@Inject
	IOrderProductInfoService orderProductInfoService;
	@Inject
	IOrderService orderService;
	@Inject
	IUserService userService;
	@Inject
	ICustomerService customerService;
	@Inject
	IAnalysisTypeService analysisTypeService;
	@Inject
	BeerEndpoint beerEndpoint;
	@Autowired
	OrderInfoValidator orderValidator;

	@RequestMapping("basket")
	public String showLoginPage(Model model) throws Exception {

		setBasket(model, new OrderProductInfo());
		return "basket";
	}

	private void setBasket(Model model, OrderProductInfo orderProductInfo) {
		Customer loggedCustomer = customerService.load(userService.getLogged()
				.getId());
		double discount = Utils
				.getDiscount(loggedCustomer, analysisTypeService);
		Order order = orderService.getBasketOrderForCustomer(loggedCustomer);

		model.addAttribute("order", new Order());
		model.addAttribute("orderProductInfo", orderProductInfo);
		if (order != null) {
			List<OrderProductInfo> orderProductInfoList = order
					.getProductList();
			double sum = 0;
			for (OrderProductInfo info : orderProductInfoList) {
				sum += info.getPrice();
				if (info.getQuantity() > info.getProduct().getQuantity()) {
					info.setQuantity(info.getProduct().getQuantity());
				}
			}

			model.addAttribute("price", String.format("%.2f", sum));
			model.addAttribute("discount", (int) (discount * 100));
			model.addAttribute("productsList", orderProductInfoList);
		} else {
			model.addAttribute("productsList",
					new ArrayList<OrderProductInfo>());
		}
		if (loggedCustomer != null) {
			AnalysisType analysisList = analysisTypeService
					.loadByCustomerID(customerService.getByUsername(
							loggedCustomer.getUsername()).getCustomerID());
			if (analysisList != null) {
				for (int i = 0; i < analysisList.getAnalysies().size(); i++) {
					if (AnalysisTypeEnum.INGREDIENT_MATCH.equals(analysisList
							.getAnalysies().get(i).getType())
							&& analysisList.getAnalysies().get(i)
									.getProductID() != null) {
						String brandURL = "<a href = \"product?id="
								+ analysisList.getAnalysies().get(i)
										.getProductID()
								+ "\">tutaj" + "</a>";
						model.addAttribute("link", brandURL);
					}
				}
			}
		}
	}

	@RequestMapping(value = "/editBasket", method = RequestMethod.POST)
	public String editBasketProduct(OrderProductInfo orderProductInfo,
			BindingResult result, Model model) {

		orderProductInfo.setProduct(productService.load(orderProductInfo
				.getProduct().getId()));

		orderValidator.validate(orderProductInfo, result);

		if (result.hasErrors()) {

			setBasket(model, orderProductInfo);

			return "basket";
		}
		Customer loggedCustomer = customerService.load(userService.getLogged()
				.getId());
		OrderProductInfo toSave = orderProductInfoService.load(orderProductInfo
				.getId());
		toSave.setQuantity(orderProductInfo.getQuantity());
		double discount = Utils
				.getDiscount(loggedCustomer, analysisTypeService);
		toSave.setPrice(toSave.getProduct().getPrice() * toSave.getQuantity()
				* (1.0 - discount));
		Order order = orderService.getBasketOrderForCustomer(loggedCustomer);
		if (order == null) {
			order = new Order();
			order.setOrderDate(new Date());
			order.setOrderID(UUID.randomUUID().toString());
			order.setCustomer(loggedCustomer);
			order.setOrderState(OrderState.BASKET);
			orderService.save(order);
		}
		orderService.addToOrder(order, toSave, discount);
		model.addAttribute("order", new Order());
		model.addAttribute("orderProductInfo", new OrderProductInfo());

		if (orderService.getBasketOrderForCustomer(loggedCustomer) != null) {
			model.addAttribute("productsList", orderService
					.getBasketOrderForCustomer(loggedCustomer).getProductList());
			double sum = 0;
			for (OrderProductInfo info : orderService
					.getBasketOrderForCustomer(loggedCustomer).getProductList()) {
				sum += info.getPrice();
			}
			BigDecimal decimal = new BigDecimal(sum);
			decimal.setScale(2, RoundingMode.HALF_UP);
			model.addAttribute("price", decimal);
			model.addAttribute("discount", (int) (discount * 100));
		} else {
			model.addAttribute("productsList",
					new ArrayList<OrderProductInfo>());
		}
		return "basket";
	}

	@RequestMapping(value = "/makeOrder", method = RequestMethod.POST)
	public String makeOrder(Order order, BindingResult result, Model model)
			throws DatatypeConfigurationException {

		Customer loggedCustomer = customerService.load(userService.getLogged()
				.getId());
		order = orderService.getBasketOrderForCustomer(loggedCustomer);
		order.setOrderState(OrderState.WAITING);
		orderService.save(order);
		OrderRequest request = new OrderRequest();
		request.setCustomerId(loggedCustomer.getCustomerID());
		request.setDate(dateToXml(order.getOrderDate()));
		request.setOrderId(order.getOrderID());
		for (OrderProductInfo info : order.getProductList()) {
			Product product = productService.load(info.getProduct().getId());
			product.setQuantity(product.getQuantity() - info.getQuantity());
			productService.save(product);
			pl.lodz.p.browarex.ws.model.OrderProductInfo inf = new pl.lodz.p.browarex.ws.model.OrderProductInfo();
			inf.setProductCode(info.getProductCode());
			inf.setProductName(info.getProductName());
			inf.setProductPrice(new BigDecimal(info.getPrice()).setScale(2,
					RoundingMode.HALF_UP));
			inf.setQuantity(info.getQuantity());
			request.getProducts().add(inf);
		}
		beerEndpoint.pushData(beerEndpoint.marshal(request));
		return "redirect:orders";
	}

	@RequestMapping(value = "/deleteItem", method = RequestMethod.POST)
	public String deleteItem(OrderProductInfo orderProductInfo,
			BindingResult result, Model model) {
		Customer loggedCustomer = customerService.load(userService.getLogged()
				.getId());
		Order order = orderService.getBasketOrderForCustomer(loggedCustomer);
		List<OrderProductInfo> list = order.getProductList();
		for (OrderProductInfo info : list) {
			if (info.getId().equals(orderProductInfo.getId())) {
				list.remove(info);
				break;
			}
		}
		order.setProductList(list);
		orderService.save(order);
		return "redirect:basket";
	}

	private XMLGregorianCalendar dateToXml(Date date)
			throws DatatypeConfigurationException {
		GregorianCalendar calendar = new GregorianCalendar();
		calendar.setTime(date);
		return DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);
	}

}
